﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Windows.Media.Media3D10
{
    /// <summary>
    /// Matériau de rendu d'une couche de nuages
    /// </summary>
    [System.Windows.Markup.ContentProperty("Brush")]
    public sealed class CloudsMaterial : Material
    {
        #region Dependency Properties
        /// <summary>
        /// Identifie la propriété de dépendance <see cref="BrushProperty"/>.
        /// </summary>
        public static readonly DependencyProperty BrushProperty =
            DependencyProperty.Register("Brush", typeof(Brush), typeof(CloudsMaterial),
            new PropertyMetadata(new PropertyChangedCallback(Brush_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="HeightProperty"/>.
        /// </summary>
        public static readonly DependencyProperty HeightProperty =
            DependencyProperty.Register("Height", typeof(float), typeof(CloudsMaterial),
            new PropertyMetadata(0.01f, new PropertyChangedCallback(Height_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="OpacityProperty"/>.
        /// </summary>
        public static readonly DependencyProperty OpacityProperty =
            DependencyProperty.Register("Opacity", typeof(float), typeof(CloudsMaterial),
            new PropertyMetadata(1.0f, new PropertyChangedCallback(Opacity_PropertyChanged)));

        /// <summary>
        /// Identifie la propriété de dépendance <see cref="RevolutionTimeProperty"/>.
        /// </summary>
        public static readonly DependencyProperty RevolutionTimeProperty =
            DependencyProperty.Register("RevolutionTime", typeof(TimeSpan), typeof(CloudsMaterial),
            new PropertyMetadata(new PropertyChangedCallback(RevolutionTime_PropertyChanged)));
        #endregion

        #region Dependency Properties Callbacks
        private static void Brush_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as CloudsMaterial).OnBrushChanged(e);
        }

        private static void Height_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as CloudsMaterial).OnHeightChanged(e);
        }

        private static void Opacity_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as CloudsMaterial).OnOpacityChanged(e);
        }

        private static void RevolutionTime_PropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(sender, null))
                throw new ArgumentNullException("sender");
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            (sender as CloudsMaterial).OnRevolutionTimeChanged(e);
        }
        #endregion

        #region Constructors
        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="CloudsMaterial"/>
        /// </summary>
        public CloudsMaterial()
            : base()
        {
            this.RevolutionTime = TimeSpan.Zero;
        }
        #endregion

        #region Data
        private const string g_fCloudHeight = "g_fCloudHeight";
        private const string g_fCloudOpacity = "g_fCloudOpacity";
        private const string g_fCloudSpeed = "g_fCloudSpeed";
        private const string cloud_texture = "cloud_texture";
        #endregion

        #region Public Properties
        /// <summary>
        /// Texture de nuages
        /// </summary>
        public Brush Brush
        {
            get { return (Brush)GetValue(BrushProperty); }
            set { SetValue(BrushProperty, value); }
        }

        /// <summary>
        /// Hauteur des nuages
        /// </summary>
        public float Height
        {
            get { return (float)GetValue(HeightProperty); }
            set { SetValue(HeightProperty, value); }
        }

        /// <summary>
        /// Opacité
        /// </summary>
        public float Opacity
        {
            get { return (float)GetValue(OpacityProperty); }
            set { SetValue(OpacityProperty, value); }
        }

        /// <summary>
        /// Durée de révolution autour du la sphère
        /// </summary>
        public TimeSpan RevolutionTime
        {
            get { return (TimeSpan)GetValue(RevolutionTimeProperty); }
            set { SetValue(RevolutionTimeProperty, value); }
        }
        #endregion

        #region Private Members
        private void OnBrushChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(cloud_texture).AsResource().SetTexture(Brush);
        }

        private void OnHeightChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(g_fCloudHeight).AsScalar().Set(Height);
        }

        private void OnOpacityChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(g_fCloudOpacity).AsScalar().Set(Opacity);
        }

        private void OnRevolutionTimeChanged(DependencyPropertyChangedEventArgs e)
        {
            if (object.ReferenceEquals(e, null))
                throw new ArgumentNullException("e");
            if (!object.ReferenceEquals(this.Effect, null))
                this.Effect.GetVariableByName(g_fCloudSpeed).AsScalar().Set(RevolutionTime.Milliseconds);
        }
        #endregion

        #region Material Overrides
        /// <summary>
        /// Appelé lors de l'initialisation de l'effet associé
        /// </summary>
        /// <param name="effect">Effet associé</param>
        protected override void OnInitEffect(SlimDX.Direct3D10.Effect effect)
        {
            base.OnInitEffect(effect);
            if (!object.ReferenceEquals(this.Effect, null))
            {
                this.Effect.GetVariableByName(cloud_texture).AsResource().SetTexture(Brush);
                this.Effect.GetVariableByName(g_fCloudHeight).AsScalar().Set(Height);
                this.Effect.GetVariableByName(g_fCloudOpacity).AsScalar().Set(Opacity);
                this.Effect.GetVariableByName(g_fCloudSpeed).AsScalar().Set(RevolutionTime.Milliseconds);
            }
        }
        #endregion

        #region Freezable Implementation
        /// <summary>
        /// Une fois implémentée dans une classe dérivée, initialise une nouvelle instance de la classe dérivée <see cref="Freezable"/>. 
        /// </summary>
        /// <returns>Nouvelle instance.</returns>
        protected override Freezable CreateInstanceCore()
        {
            return new CloudsMaterial();
        }
        #endregion
    }
}
